use std::sync::Arc;

use crate::error::ServerError;
use crate::protocol::data_schema::DataTrait;
use crate::protocol::type_schema::TypeSchema;
use crate::abstractions::data::Data;
use crate::tasks::task::Task;

pub type TaskFunc = Arc<dyn Fn(Data) -> Result<Data, ServerError> + Send + Sync>;
// pub type TaskFunc = fn(Data) -> Result<Data, ServerError>;

/// 格式：（服务ID，任务名）
pub type TaskId = (String, String);

pub enum TaskAbstraction {
    Builtin(String, TypeSchema, TypeSchema, TaskFunc),
    FromService(TaskId),
    SignalSupport(String, TypeSchema, TypeSchema, TaskFunc, Vec<(u64, u64)>)
}

use TaskAbstraction::*;

macro_rules! input_format_error {
    ($name: expr) => {
        // panic!("input format error: {}", $name)
        Err(ServerError::DataError(format!("input format error: {}", $name)))
    };
}

macro_rules! output_format_error {
    ($name: expr) => {
        // panic!("output format error: {}", $name)
        Err(ServerError::DataError(format!("output format error: {}", $name)))
    };
}

impl TaskAbstraction {
    pub fn is_builtin(&self) -> bool {
        match self {
            Builtin(_, _, _, _) => true,
            FromService(_) => false,
            SignalSupport(_, _, _, _, _) => false
        }
    }

    pub fn name(&self) -> &String {
        match self {
            Builtin(name, _, _, _) => name,
            FromService(name) => &name.1,
            SignalSupport(name, _, _, _, _) => name
        }
    }

    pub fn tin(&self) -> &TypeSchema {
        match self {
            Builtin(_, tin, _, _) => tin,
            FromService(_) => unreachable!(),
            SignalSupport(_, tin, _, _, _) => tin
        }
    }

    pub fn tout(&self) -> &TypeSchema {
        match self {
            Builtin(_, _, tout, _) => tout,
            FromService(_) => unreachable!(),
            SignalSupport(_, _, tout, _, _) => tout
        }
    }

    pub fn flatten(self) -> (String, TypeSchema, TypeSchema, TaskFunc) {
        match self {
            Builtin(name, tin, tout, fun) => (name, tin, tout, fun),
            FromService(_) => unreachable!(),
            SignalSupport(name, tin, tout, fun, _) => (name, tin, tout, fun)
        }
    }

    pub fn flatten_clone(&self) -> (String, TypeSchema, TypeSchema, TaskFunc) {
        match self {
            Builtin(name, tin, tout, fun) => (name.clone(), tin.clone(), tout.clone(), fun.clone()),
            FromService(_) => unreachable!(),
            SignalSupport(name, tin, tout, fun, _) => (name.clone(), tin.clone(), tout.clone(), fun.clone())
        }
    }

    pub fn qfun(self) -> (TypeSchema, TaskFunc) {
        match self.flatten() {
            (name, tin, tout, fun) => {
                let novel_func = Arc::new(move |input: Data| {
                    if !input.qualify(&tin) {
                        input_format_error!(name.clone())
                    } else {
                        fun(input)
                    }
                });
                (tout, novel_func)
            }
        }
    }

    pub fn funq(self) -> (TypeSchema, TaskFunc) {
        match self.flatten() {
            (name, tin, tout, fun) => {
                let novel_func = Arc::new(move |input: Data| {
                    let res = fun(input)?;
                    if !res.qualify(&tout) {
                        output_format_error!(name)
                    } else {
                        Ok(res)
                    }
                });
                (tin, novel_func)
            }
        }
    }

    pub fn qfunq(self) -> TaskFunc {
        match self.flatten() {
            (name, tin, tout, fun) => {
                Arc::new(move |input: Data| {
                    if !input.qualify(&tin) {
                        input_format_error!(name)
                    } else {
                        let res = fun(input)?;
                        if !res.qualify(&tout) {
                            output_format_error!(name)
                        } else {
                            Ok(res)
                        }
                    }
                })
            }
        }
    }

    pub fn qfunq_clone(&self) -> TaskFunc {
        match self.flatten_clone() {
            (name, tin, tout, fun) => {
                Arc::new(move |input: Data| {
                    if !input.qualify(&tin) {
                        input_format_error!(name)
                    } else {
                        let res = fun(input)?;
                        if !res.qualify(&tout) {
                            output_format_error!(name)
                        } else {
                            Ok(res)
                        }
                    }
                })
            }
        }
    }

    pub fn service_id(&self) -> &String {
        match self {
            FromService(name) => &name.0,
            _ => unreachable!()
        }
    }

    pub fn task_name(&self) -> &String {
        match self {
            FromService(name) => &name.1,
            _ => unreachable!()
        }
    }
}